package pl.edu.agh.administracja.networkconfig.model.util;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import pl.edu.agh.administracja.networkconfig.model.AuthenticationType;
import pl.edu.agh.administracja.networkconfig.model.WirelessInterfaceConfig;
import pl.edu.agh.administracja.networkconfig.model.WirelessNetwork;

/**
 * Wyciaga liste dostepnych sieci bezprzewodowych.
 *
 * @author piotrek
 */
public class WirelessNetworkResolver {

    private WirelessNetworkResolver() {
    }
    private static WirelessNetwork currentWirelessNetwork;
    private static List<WirelessNetwork> list;
    private static LineParser[] parsers = {
        new MainLineParser(),
        new ESSIDLineParser(),
        new BitRatesLineParser(),
        new QualityLineParser(),
        new EncryptionKeyLineParser(),
        new WPALineParser()
    };

    public static List<WirelessNetwork> getAvailableNetworks(WirelessInterfaceConfig wic) {

        list = new ArrayList<WirelessNetwork>();

        String result = ShellExecutor.executeSynchronously("iwlist " + wic.getName() + " scanning");

        result = "Cell 01 - Address: 00:1B:38:AD:67:AB\n"
                + " Channel:3\n"
                + " Frequency:2.422 GHz (Channel 3)\n"
                + " Quality=32/70  Signal level=-78 dBm\n"
                + " Encryption key:on\n"
                + " ESSID:\"lubiewczoko\"\n"
                + " Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s\n"
                + " Mode:Master\n"
                + " Extra:tsf=000000a77f0101c1\n"
                + " Extra: Last beacon: 11008ms ago\n"
                + " IE: Unknown: 000B6C7562696577637A6F6B6F\n"
                + " IE: Unknown: 010482848B96\n"
                + " IE: Unknown: 030103\n"
                + " IE: WPA Version 1\n"
                + " Group Cipher : TKIP\n"
                + " Pairwise Ciphers (1) : TKIP\n"
                + " Authentication Suites (1) : PSK\n";

        for (String line : result.split("\n")) {
            parseLine(line);
        }

        if (currentWirelessNetwork != null) {
            list.add(currentWirelessNetwork);
        }

        // TODO zrob mnie
        WirelessNetwork fakeNetwork = new WirelessNetwork();
        fakeNetwork.setESSID("Lubie placki");
        fakeNetwork.setAuthenticationType(AuthenticationType.OPEN);
        fakeNetwork.setQuality("poor");

        list.add(fakeNetwork);
        try {
            // TODO wyrzucic
            // test wpolbieznosci
            //Thread.sleep(5000);
        } catch (Exception ex) {
            Logger.getLogger(WirelessNetworkResolver.class.getName()).log(Level.SEVERE, null, ex);
        }

        return list;
    }

    private static void parseLine(String line) {

        for (LineParser parser : parsers) {
            if (parser.parseLine(line)) {
                break;
            }
        }

    }

    private static class MainLineParser extends LineParser {

        private static final String REGEX = "Cell [0-9]{2} - Address: [0-9a-fA-F:]{17}[ ]*";

        public MainLineParser() {
            super(REGEX);
        }

        @Override
        protected void processResults(Matcher matcher) {
            if (currentWirelessNetwork != null) {
                list.add(currentWirelessNetwork);
            }

            currentWirelessNetwork = new WirelessNetwork();
        }
    }

    private static class ESSIDLineParser extends LineParser {

        private static final String REGEX = "[ ]+ESSID:\"([a-zA-Z0-9_ ]+)\"[ ]*";

        public ESSIDLineParser() {
            super(REGEX);
        }

        @Override
        public boolean parseLine(String line) {
            return super.parseLine(line);
        }

        @Override
        protected void processResults(Matcher matcher) {
            currentWirelessNetwork.setESSID(matcher.group(1));
        }
    }

    private static class BitRatesLineParser extends LineParser {

        private static final String REGEX = "[ ]+Bit Rates:(.*)";

        public BitRatesLineParser() {
            super(REGEX);
        }

        @Override
        protected void processResults(Matcher matcher) {
            currentWirelessNetwork.setBitRates(matcher.group(1).split(";(?:[ ])?"));
        }
    }

    private static class QualityLineParser extends LineParser {

        private static final String REGEX = "[ ]+Quality=([0-9]+/[0-9]+).*";

        public QualityLineParser() {
            super(REGEX);
        }

        @Override
        protected void processResults(Matcher matcher) {
            currentWirelessNetwork.setQuality(matcher.group(1));
        }
    }

    private static class EncryptionKeyLineParser extends LineParser {

        private static final String REGEX = "[ ]+Encryption key:(.*)";

        public EncryptionKeyLineParser() {
            super(REGEX);
        }

        @Override
        protected void processResults(Matcher matcher) {
            String _switch = matcher.group(1);
            if ("on".equals(_switch)) {
                currentWirelessNetwork.setAuthenticationType(AuthenticationType.WEP);
            }

            if ("off".equals(_switch)) {
                currentWirelessNetwork.setAuthenticationType(AuthenticationType.OPEN);
            }
        }
    }

    private static class WPALineParser extends LineParser {

        private static final String REGEX = "[ ]+IE: WPA Version ([1-2]).*";

        public WPALineParser() {
            super(REGEX);
        }

        @Override
        protected void processResults(Matcher matcher) {
            currentWirelessNetwork.setAuthenticationType(matcher.group(1).equals("1") ? AuthenticationType.WPA : AuthenticationType.WPA2);
        }
    }

    private static abstract class LineParser {

        private Pattern pattern;

        public LineParser(String regex) {
            pattern = Pattern.compile(regex);
        }

        public boolean parseLine(String line) {
            Matcher matcher = pattern.matcher(line);
            if (!matcher.matches()) {
                return false;
            }

            processResults(matcher);

            return true;
        }

        protected abstract void processResults(Matcher matcher);
    }
}
